﻿using System.Collections;
using System.Collections.Generic;
using Fine.Collections.Intf;
using Fine.TestUtils;
using NUnit.Framework;



namespace Fine.Collections.Const
{


    [TestFixture]
    public class ConstList_test
    {

        #region BASIC

        [Test]
        public void Test_Basic0()
        {
            ConstList<long> list = CC1.ListOf(new long[0]);
            
            list.Count      ._equals_(0);
            list.IsEmpty    ._equals_(true);
            list.IsNotEmpty ._equals_(false);
        }


        [Test]
        public void Test_Basic1()
        {
            ConstList<long> list = CC1.ListOf(1111L);
            
            list.Count      ._equals_(1);
            list.IsEmpty    ._equals_(false);
            list.IsNotEmpty ._equals_(true);

            list[1]         ._equals_(1111L);
        }


        [Test]
        public void Test_Basic3()
        {
            ConstList<long> list = CC1.ListOf(444L, 777L, 999L);

            list[1] ._equals_(444L);
            list[2] ._equals_(777L);
            list[3] ._equals_(999L);
        }


        #endregion



        #region FIRST and LAST


        [Test]
        public void Test_FirstLast_Basic()
        {
            var list = CC1.ListOf("One", "Two", "Three");
            list.First ._equals_("One");
            list.Last  ._equals_("Three");
        }

        [Test]
        public void Test_FirstLast_One()
        {
            var list = CC1.ListOf("One");
            list.First ._equals_("One");
            list.Last  ._equals_("One");
        }

        [Test]
        [ExpectedException(typeof(CollectionIsEmptyException))]
        public void Test_First_Empty()
        {
            var list = CC1.ListOf<object>();
            list.First  ._unreachable_();
        }

        [Test]
        [ExpectedException(typeof(CollectionIsEmptyException))]
        public void Test_Last_Empty()
        {
            var list = CC1.ListOf<object>();
            list.Last ._unreachable_();
        }


        #endregion



        #region FIRST and LAST INDEX

        [Test]
        public void Test_FirstLastIndex_Basic()
        {
            var list = CC.ListOf(2001,
                                 "Закрыт Варшавский вокзал в СПб",
                                 "Краш-тест электропоезда в СПб",
                                 "Открыт Ладожский вокзал в СПб",
                                 "Основана Приднестровская железная дорога");
            list.FirstIndex ._equals_(2001);
            list.LastIndex ._equals_(2004);
        }

        #endregion



        #region THIS[] INDEXER

        [Test]
        public void Test_This_Value()
        {
            var list = CC.ListOf<byte>(1001, 11, 22, 33, 44, 55);
            list[1001] ._equals_((byte)11);
            list[1002] ._equals_((byte)22);
            list[1003] ._equals_((byte)33);
            list[1004] ._equals_((byte)44);
            list[1005] ._equals_((byte)55);
        }

        [Test]
        public void Test_This_Ref()
        {
            var x = new object();
            var y = new object();
            Assert.AreNotSame(x,y);

            var list = CC1.ListOf<object>(x,y);
            list[1] ._same_(x);
            list[2] ._same_(y);
        }

        [Test]
        public void Test_Predicate()
        {
            var list = CC1.ListOf(111, 333, 555, 777, 999);
            list[x => x > 100 && x < 200]  ._equals_(111);
            list[x => x > 500 && x < 600]  ._equals_(555);
            list[x => x > 900 && x < 1000] ._equals_(999);
        }

        [Test]
        public void Test_Predicate_First()
        {
            var list = CC1.ListOf(111,333,555,777,999);
            list[x => x > 200 && x < 800]  ._equals_(333);
        }

        [Test]
        public void Test_Predicate_NotFound_Value()
        {
            var list = CC1.ListOf(111,333,555);
            list[x => x > 1000]  ._equals_(0);
        }

        [Test]
        public void Test_Predicate_NotFound_Ref()
        {
            var list = CC1.ListOf("aaa", "bbbb", "ccccc");
            list[s => s.Length == 99]  ._equals_(null);
        }


        [TestCase(14)]
        [TestCase(18)]
        [TestCase(int.MinValue)]
        [TestCase(int.MinValue+1)]
        [TestCase(int.MaxValue)]
        [ExpectedException(typeof(IndexOutOfBoundsException))]
        public void Test_BadIndex(int index)
        {
            var list = CC.ListOf(15, "eins", "zwei", "drei");
            var z = list[index];
        }


        #endregion



        #region FIND

        [TestCase("1111", Result = "111111")]
        [TestCase("3333", Result = "333333")]
        [TestCase("5555", Result = "555555")]
        [TestCase("7777", Result = null)]
        public string Test_Find_Basic(string prefix)
        {
            string s1 = "111"+"111",
                   s2 = "222"+"222",
                   s3 = "333"+"333",
                   s4 = "444"+"444",
                   s5 = "555"+"555";
            var list = CC1.ListOf(s1,s2,s3,s4,s5);
            return list.Find(val => val.StartsWith(prefix)).Item;
        }

        #endregion



        #region COUNT OF

        [TestCase(11)]
        [TestCase(22)]
        [TestCase(33)]
        [TestCase(44)]
        public void Test_CountOf_Item_Basic(int x)
        {
            var list = CC1.ListOf<int>(11,22,33,44);
            int cnt = list.CountOf(x);

            cnt._equals_(1);
        }


        [Test]
        public void Test_CountOf_Item_RefByEqual()
        {
            object x = new string('a',6);
            object y = "Aaaaaa".ToLower();

            var list = CC1.ListOf("ooo", x, y, "uuu");
            int cnt = list.CountOf("aaaaaa");

            cnt ._equals_(2);
        }


        [Test]
        public void Test_CountOf_Item_Value()
        {
            var list = CC1.ListOf<int>(11, 22+22, 44, 99);
            int cnt = list.CountOf(44);

            cnt ._equals_(2);
        }


        [Test]
        public void Test_CountOf_Item_NotFound()
        {
            var list = CC1.ListOf<int>(11, 22, 33, 44);
            int cnt = list.CountOf(20);

            cnt ._equals_(0);
        }

        #endregion



        #region INDEX OF and CONTAINS

        [Test]
        public void Test_IndexOf_Basic()
        {
            var list = CC.ListOf(11, 1001, 1002, 1003, 1004);
            list.IndexOf(1001) ._equals_(11);
            list.IndexOf(1002) ._equals_(12);
            list.IndexOf(1003) ._equals_(13);
            list.IndexOf(1004) ._equals_(14);
        }

        [Test]
        public void Test_IndexOf_NotFound()
        {
            var list = CC.ListOf(-1, 1001, 1002, 1003);
            list.IndexOf(-999) ._equals_(int.MinValue);
        }

        [Test]
        public void Test_IndexOf_Null()
        {
            var list = CC.ListOf(-1, "Домра", "Балалайка", "Зурна");
            list.IndexOf((string)null) ._equals_(int.MinValue);
        }


        [TestCase("До", Result = -1)]
        [TestCase("Ба", Result =  0)]
        [TestCase("Зу", Result = +1)]
        [TestCase("Ла", Result = int.MinValue)]
        public int Test_IndexOf_Predicate(string prefix)
        {
            var list = CC.ListOf(-1, "Домра", "Балалайка", "Зурна");
            return list.IndexOf(str => str.StartsWith(prefix));
        }


        [TestCase("До", -1000, Result = -1)]
        [TestCase("До", -1,    Result = -1)]
        [TestCase("До",  0,    Result = int.MinValue)]
        [TestCase("Ба",  0,    Result = 0)]
        [TestCase("Ба", +1,    Result = int.MinValue)]
        [TestCase("Зу", +1000, Result = int.MinValue)]
        public int Test_IndexOf_PredicateFromIndex(string prefix, int startIndex)
        {
            var list = CC.ListOf(-1, "Домра", "Балалайка", "Зурна");
            return list.IndexOf(str => str.StartsWith(prefix), startIndex);
        }


        [TestCase("Домра", Result = true)]
        [TestCase("Зурна", Result = true)]
        [TestCase("Лабуда", Result = false)]
        public bool Test_Contains(string str)
        {
            var list = CC.ListOf(-1, "Домра", "Балалайка", "Зурна");
            return list.Contains(str);
        }


        [TestCase("Домра", Result = true)]
        [TestCase("Зурна", Result = true)]
        [TestCase("Лабуда", Result = false)]
        public bool Test_Contains_Predicate(string str)
        {
            var list = CC.ListOf(-1, "Домра", "Балалайка", "Зурна");
            return list.Contains(item => item.Equals(str));
        }

        #endregion



        #region PROCESS ALL

        [Test]
        public void Test_ProcessAll_AllAreProcessed()
        {
            var list = CC1.ListOf(7, 6, 5, 4, 3, 2, 1, 0);
            uint bits = 0;

            list.ProcessAll(bit => bits |= (1u << bit));

            bits.   _equals_(0xFFu);
        }

        #endregion



        #region TO SET

        [Test]
        public void Test_ToSet_Trivial()
        {
            var list = CC1.ListOf(1111, 2222, 3333, 4444);
            var set = list.ToSet();

            set        ._contains_(1111, 2222, 3333, 4444);
            set.Count  ._equals_(4);
        }


        [Test]
        public void Test_ToSet_Reorder()
        {
            var list = CC1.ListOf(6666, 2222, 7777, 3333, 1111, 9999);
            var set = list.ToSet();

            set        ._contains_(1111, 2222, 3333, 6666, 7777, 9999);
            set.Count  ._equals_(6);
        }


        [Test]
        public void Test_ToSet_Distinct()
        {
            var list = CC1.ListOf(1111, 2222, 3333, 4444, 2222, 3333);
            var set = list.ToSet();

            set        ._contains_(1111, 2222, 3333, 4444);
            set.Count  ._equals_(4);
        }


        [Test]
        public void Test_ToSet_PreserveFirstIndex()
        {
            CC0.ListOf(6666,7777).ToSet().FirstIndex  ._equals_(0);
            CC1.ListOf(6666,7777).ToSet().FirstIndex  ._equals_(1);
            CC.ListOf(-44, 6666,7777).ToSet().FirstIndex  ._equals_(-44);
        }


        [Test]
        public void Test_ToSet_Empty()
        {
            var list = CC1.ListOf<ulong>();
            var set = list.ToSet();
            set.IsEmpty  ._true_();
            set.Count    ._equals_(0);
        }

        #endregion



        #region ITERATORS

        [Test]
        public void Test_Iterator_Basic()
        {
            var list = CC1.ListOf(1111, 2222, 3333, 4444);
            var it = list.TakeIterator();
            
            it.Next()   ._true_();
            it.Current  ._equals_(1111);
            it.Next()   ._true_();
            it.Current  ._equals_(2222);
            it.Next()   ._true_();
            it.Current  ._equals_(3333);
            it.Next()   ._true_();
            it.Current  ._equals_(4444);
            it.Next()   ._false_();
        }


        [Test]
        public void Test_Enumerator_Basic()
        {
            var list = CC1.ListOf(1111, 2222, 3333, 4444);
            var er = (list as IEnumerable<int>).GetEnumerator();

            er.MoveNext()   ._true_();
            er.Current      ._equals_(1111);
            er.MoveNext()   ._true_();
            er.Current      ._equals_(2222);
            er.MoveNext()   ._true_();
            er.Current      ._equals_(3333);
            er.MoveNext()   ._true_();
            er.Current      ._equals_(4444);
            er.MoveNext()   ._false_();
        }

        [Test]
        public void Test_Enumerator_Old()
        {
            var list = CC1.ListOf(1111, 2222, 3333, 4444);
            var er = (list as IEnumerable).GetEnumerator();

            er.MoveNext()   ._true_();
            er.Current      ._equals_(1111);
            er.MoveNext()   ._true_();
            er.Current      ._equals_(2222);
            er.MoveNext()   ._true_();
            er.Current      ._equals_(3333);
            er.MoveNext()   ._true_();
            er.Current      ._equals_(4444);
            er.MoveNext()   ._false_();
        }

        

        #endregion



        #region HASH CODE

        [Test]
        public void Test_HashCode_Basic()
        {
            var a = CC0.ListOf("tea", "coffee", "cocoa");
            a.GetHashCode(); // it should not throw exeptions
        }


        [Test]
        public void Test_HashCode_WithNull()
        {
            var a = CC0.ListOf("tea", null, "coffee");
            a.GetHashCode(); // it should not throw exeptions
        }


        [Test]
        public void Test_HashCode_Empty()
        {
            var a = CC0.ListOf<string>();
            int hc = a.GetHashCode(); 
            hc ._equals_(0);
        }


        #endregion



        #region EQUALS

        [Test]
        public void Test_EqualsBasic()
        {
            var a = CC1.ListOf(444L, 666L, 777L, 999L);
            var b = CC1.ListOf(444L, 666L, 777L, 999L);

            (a == b) ._true_();
            (a != b) ._false_();
        }


        [Test]
        public void Test_EqualsSame()
        {
            var a = CC1.ListOf(444L, 666L);
            var b = a;

            (a == b) ._true_();
            (a != b) ._false_();
        }


        [Test]
        public void Test_EqualsBasic2()
        {
            var a = CC1.ListOf(444L, 666L, 777L, 999L);
            var b = CC1.ListOf(444L, 666L, 777L, 999L);

            a.Equals(b) ._true_();
        }


        [Test]
        public void Test_EqualsSame2()
        {
            var a = CC1.ListOf(444L, 666L);
            var b = a;

            a.Equals(b) ._true_();
        }


        [Test]
        public void Test_EqualsDifferentFirstIndices()
        {
            var a = CC1.ListOf(444L, 666L);
            var b = CC0.ListOf(444L, 666L);

            (a == b) ._false_();
            (a != b) ._true_();
        }


        [Test]
        public void Test_EqualsDifferentFirstCount()
        {
            var a = CC0.ListOf(444L, 555L, 666L);
            var b = CC0.ListOf(444L, 666L);

            (a == b) ._false_();
            (a != b) ._true_();
        }


        [Test]
        public void Test_EqualsWithNull()
        {
            var a = CC0.ListOf("tea", null, "coffee");
            var b = CC0.ListOf("tea", null, "coffee");

            (a == b) ._true_();
            (a != b) ._false_();
        }


        [Test]
        public void Test_EqualsNull()
        {
            var a = CC0.ListOf(111L);
            ConstList<long> b = null;

            (a == b) ._false_();
            (a != b) ._true_();
        }


        #endregion



        #region ICollection non-functioning methods

        [Test]
        [ExpectedException(typeof(AttemptToModifyConstantException))]
        public void Test_Add()
        {
            var collection = CC1.ListOf(333) as ICollection<int>;
            collection.Add(444);
        }

        [Test]
        [ExpectedException(typeof(AttemptToModifyConstantException))]
        public void Test_Remove()
        {
            var collection = CC1.ListOf(333) as ICollection<int>;
            collection.Remove(333);
        }

        [Test]
        [ExpectedException(typeof(AttemptToModifyConstantException))]
        public void Test_Clear()
        {
            var collection = CC1.ListOf(333) as ICollection<int>;
            collection.Clear();
        }

        #endregion



        #region OTHER METHODS

        [Test]
        public void Test_IsReadOnly()
        {
            var collection = CC1.ListOf(555) as ICollection<int>;
            collection.IsReadOnly ._true_();
        }


        [Test]
        public void Test_CopyTo_Basic()
        {
            var list = CC.ListOf(1000, "aaa", "bbb", "ccc");
            var array = new string[7];
            ((ICollection<string>) list).CopyTo(array, 2);

            array[2]  ._equals_("aaa");
            array[3]  ._equals_("bbb");
            array[4]  ._equals_("ccc");
        }


        [Test]
        public void Test_ToString_Basic()
        {
            var list = CC1.ListOf(111L, 222L, 333L);

            list.ToString()  ._equals_("[111,222,333]");
        }


        [Test]
        public void Test_ToString_Empty()
        {
            var list = CC1.ListOf<ulong>();

            list.ToString()  ._equals_("[]");
        }


        #endregion



        #region DEBUG VIEW

        [Test]
        public void Test_DebugView_0()
        {
            ConstList<long> list = CC1.ListOf<long>();

            var debugView = new ConstArrayDebugView<long>(list);
            var debugItems = debugView.Items;

            debugItems.Length  ._equals_(0);
        }


        [Test]
        public void Test_DebugView_3()
        {
            ConstList<long> list = CC1.ListOf(555L, 777L, 999L);

            var debugView = new ConstArrayDebugView<long>(list);
            var debugItems = debugView.Items;

            debugItems.Length  ._equals_(3);
            debugItems         ._array_(555L, 777L, 999L);
        }

        #endregion

    }


}